#pragma once
#include "..\typedefs\typedefs.h"
#include "..\macros\assertions.h"

namespace NEngine {

// ================================================================================================
// Templated ClinkedList basic linked list
// ================================================================================================
template<typename T> class CLinkedList {

protected:

    // --------------------------------------------------------------------------------------------
    // Basic templated node to work with this linked list
    // --------------------------------------------------------------------------------------------
    struct SNode {
        T value; 
        SNode* next;
    };


    SNode* head;
    SNode* tail;
    SNode* curnode;

    uint size;
    bool end;

public:

    // -- default constructor/destructor
    CLinkedList();
    ~CLinkedList();

    // -- public methods
    SNode* GetHead();
    uint GetSize();
    T& GetNext();
    bool EndOfList();
    void Reset();
    void Grow(T value);
};

// ================================================================================================
// Default Constructor
// ================================================================================================
template<typename T> CLinkedList<T>::CLinkedList() : head(NULL), tail(NULL), curnode(NULL),
                                                     size(0), end(true) {
}

template<typename T> CLinkedList<T>::~CLinkedList() {
    SNode* ccurnode = head;

    while(ccurnode != NULL) {
        SNode* nextnode = ccurnode->next;
    	delete ccurnode;
    	ccurnode = nextnode;
        --size;
    }
}

// ================================================================================================
// Accessors
// ================================================================================================
template<typename T> typename CLinkedList<T>::SNode* CLinkedList<T>::GetHead() {
    return head;
}

template<typename T> uint CLinkedList<T>::GetSize() {
    return size;
}

// ================================================================================================
// Get the next element in the list
// ================================================================================================
template<typename T> T& CLinkedList<T>::GetNext() { 
    SNode* retnode = curnode;
    if(curnode->next == NULL) {
        end = true;
        return retnode->value;
    }

    curnode = curnode->next;
    return retnode->value;
}

// ================================================================================================
// See if we are at the end of the list
// ================================================================================================
template<typename T> bool CLinkedList<T>::EndOfList() {
    return end;
}

// ================================================================================================
// Reset the curnode to the head
// ================================================================================================
template<typename T> void CLinkedList<T>::Reset() {
    curnode = head;
    end = false;
}

// ================================================================================================
// Grow the linked list
// ================================================================================================
template<typename T> void CLinkedList<T>::Grow(T value) {
    if(head == NULL && tail == NULL) {
        end = false;
        curnode = head = tail = new SNode;
        head->value = value;
        head->next = NULL;
        ++size;

        return;
    }

    SNode* cache = tail;
    tail = new SNode;
    cache->next = tail;
    tail->value = value;
    tail->next = NULL;
    ++size;
}

} // -- namespace NEngine